#ifndef __Y64_GATE_H__
#define __Y64_GATE_H__

// intel处理器支持的异常/中断
/*
向量号      助记符      异常/中断描述       异常/中断类型       错误码          触发源
--------------------------------------------------------------------------------------------------
0           #DE         除法错误            错误            No              DIV或IDIV指令
1           #DB         调试异常            错误/陷阱        No              仅供Intel处理器使用
2           -           NMI中断            中断            No              不可屏蔽中断
3           #BP         断点异常            陷阱            No              INT 3指令
4           #OF         溢出异常            陷阱            No              INTO指令
5           #BR         越界异常            错误            No              BOUND指令
6           #UD         无效/未定义机器码     错误            No              UD2指令或保留的机器码
7           #NM         设备异常(FPU不存在)  错误            No              浮点指令WAIT/FWAIT指令
8           #DF         双重错误            终止            Yes(Zero)       任何异常、NMI中断或INTR中断
9           -           协处理器段越界（保留） 错误            No              浮点指令
10          #TS         无效的TSS段         错误            Yes             访问TSS段或任务切换
11          #NP         段不存在            错误            Yes             加载段寄存器或访问系统段
12          #SS         SS段错误            错误            Yes             栈操作或加载栈段寄存器SS
13          #GP         通用保护性异常       错误            Yes             任何内存引用和保护检测
14          #PF         页错误              错误            Yes             任何内存引用
15          -           Intel保留，勿使用    -              No              -
16          #MF         x87 FPU错误（计算错误）错误           No              x87 FPU浮点指令或WAIT/FWAIT指令
17          #AC         对齐检测            错误            Yes(Zero)       引用内存中的任何数据
18          #MC         机器检测            终止            No              如果有错误码，其与CPU类型有关
19          #XM         SIMD浮点异常        错误            No              SSE/SSE2/SSE3浮点指令
20          #VE         虚拟化异常          错误             No              违反EPT
21-31       -           Intel保留，勿使用    -              -               -
32-255      -           用户自定义中断       中断            -               外部中断或执行INT n指令

异常类型：
错误：可以被修正的异常，处理器将运行环境还原，并重新执行产生异常的指令
陷阱：可以被修正的异常，处理器将运行环境还原，跳过产生异常的指令继续执行
终止：非常严重的错误，往往无法准确提供产生异常的位置，同时不允许继续执行，一般有硬件错误或系统表存在不合逻辑、非法值
*/

/*
(3.4.5 Segment Descriptors) https://www.intel.cn/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf
Segment Descriptors
 31            24 23 22 21 20 19    16 15 14 13 12 11      8 7              0
|----------------|--|--|--|--|--------|--|-----|--|---------|----------------|
|                |  |D |  |A |  Seg.  |  |  D  |  |         |                |
|   Base 31:24   |G |/ |L |V |  Limit |P |  P  |S |   Type  |   Base 23:16   | 4
|                |  |B |  |L |  19:16 |  |  L  |  |         |                |
|----------------|--|--|--|--|---|----|--|-----|--|---------|----------------|

 31                                 16 15
|-------------------------------------|--------------------------------------|
|                                     |                                      |
|       Base Address 15:00            |        Segment Limit 15:00           | 0
|                                     |                                      |
|-------------------------------------|--------------------------------------|
    
    L - 64-bit code segment(IA-32e mode only)
    AVL - Available for use by system software，被系统软件使用，通常设置为0
    Base - Segment base address，段基地址
    D/B - Default operation size(0 = 16-bit segment; 1 = 32-bit segment)
    DPL - Descriptor privilege level
    G - Granularity,段限长的颗粒度，1-表示以4KB作为颗粒度，0-表示以字节作为颗粒度
    Limit - Segment Limit，段限长，总共20bit，置位G时，段长度为4KB~4GB，复位G时，段长度为1B~1MB
    P - Segment present，表示段已在内存中
    S - Descriptor type (0 = system; 1 = code or data)
    TYPE - Segment type，对S标志作进一步解释，S置位时，第43bit置位表示代码段，第43bit复位时表示数据段
 */

typedef struct {
    unsigned char x[8];
} st_desc_struct;

/*
(6.14.1 64-Bit Mode IDT) https://www.intel.cn/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf
IDT表项格式：
    Name            Bit             Description
-----------------------------------------------------------
    LowOffset       0~15            服务函数地址低16位
    selector        16~31           段选择子
    IST             32~33           Interrupt Stack Table
    zero            34~39           填充0
    TYPE            40~43           
    attr            40~47           属性, P, DPL, TYPE
    MiddleOffset    48~63           服务函数地址中16位
    HighOffset      64~95           服务函数地址高32位
    Reserved        96~127          保留

注：32位cpu下只是去掉了后面64位，没有IST,其它基本都一样
*/

typedef struct {
    unsigned char x[16];
} st_gate_struct;

extern st_desc_struct GDT_Table[];
extern st_gate_struct IDT_Table[];
extern unsigned int TSS64_Table[26];

// asm(汇编语句模板: 输出部分: 输入部分: 破坏描述部分)
#define _set_gate(gate_selector_addr, attr, ist, code_addr)                 \
do {    \
    unsigned long __d0, __d1;   \
    __asm__ __volatile__ (  \
        "movw %%dx, %%ax \n\t"  \
        "andq $0x7, %%rcx \n\t" \
        "addq %4, %%rcx \n\t"   \
        "shlq $32, %%rcx \n\t"  \
        "addq %%rcx, %%rax \n\t"    \
        "xorq %%rcx, %%rcx \n\t"	\
        "movl %%edx, %%ecx \n\t"	\
        "shrq $16, %%rcx \n\t"	\
        "shlq $48, %%rcx \n\t"	\
        "addq %%rcx, %%rax \n\t"    \
        "movq %%rax, %0 \n\t"	\
        "shrq $32, %%rdx \n\t"	\
        "movq %%rdx, %1	\n\t"	\
        :"=m"(*((unsigned long *)(gate_selector_addr)))	,					\
         "=m"(*(1 + (unsigned long *)(gate_selector_addr))),"=&a"(__d0),"=&d"(__d1)		\
        :"i"(attr << 8),									\
         "3"((unsigned long *)(code_addr)),"2"(0x8 << 16),"c"(ist)				\
        :"memory"		\
    );  \
} while(0)

#define load_TR(n)  \
do {    \
    __asm__ __volatile__(   \
        "ltr %%ax"  \
        :   \
        :"a"(n << 3)    \
        :"memory"   \
    );  \
} while(0)

inline void set_intr_gate(unsigned int n, unsigned char ist, void *addr);

inline void set_trap_gate(unsigned int n, unsigned char ist, void *addr);

inline void set_system_gate(unsigned int n, unsigned char ist, void *addr);

inline void set_system_intr_gate(unsigned int n, unsigned char ist, void *addr);

void set_tss64(unsigned long rsp0, unsigned long rsp1, unsigned long rsp2, unsigned long ist1, unsigned long ist2, unsigned long ist3, unsigned long ist4, unsigned long ist5, unsigned long ist6, unsigned long ist7);

#endif